package com.zxiw.zxedu.cmd;

import android.util.Log;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;

import javax.crypto.Cipher;

/**
 * RSA瀹夊叏缂栫爜缁勪�?
 *
 * @author 姊佹�?
 * @version 1.0
 */
public abstract class RSACoder extends Coder {

    private static final String TAG = RSACoder.class.getSimpleName();

    /**
     * 鏁板瓧绛惧悕
     * 瀵嗛挜绠楁硶
     */
    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 鏁板瓧绛惧悕
     * 绛惧�?/楠岃瘉绠楁硶
     */
    public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    /**
     * 鍏�?
     */
    private static final String PUBLIC_KEY = "RSAPublicKey";

    /**
     * 绉侀�?
     */
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /**
     * 妯�?�紡
     * */
    private static final String MODE = "RSA/ECB/PKCS1Padding";

    /**
     * RSA瀵嗛挜闀垮害 榛樿�?1024浣嶏�?
     *  瀵嗛挜闀垮害蹇呴』鏄�?64鐨勫�嶆暟锛�?
     *  鑼冨洿鍦�?512鑷�65536浣嶄箣闂淬��?
     */
    private static final int KEY_SIZE = 1024;
    
    private static final int MAX_ENCRYPT_BLOCK = 117;  

    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 绛惧�?
     *
     * @param data
     *            寰呯鍚嶆暟鎹�
     * @param privateKey
     *            绉侀�?
     * @return byte[] 鏁板瓧绛惧悕
     * @throws Exception
     */
    public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {

        // 杞崲绉�?挜鏉愭枡
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);

        // 瀹炰緥鍖栧瘑閽ュ伐鍘�?
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

        // 鍙栫閽ュ寵瀵硅�?
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        // 瀹炰緥鍖朣ignature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);

        // 鍒濆鍖朣ignature
        signature.initSign(priKey);

        // 鏇存�?
        signature.update(data);

        // 绛惧�?
        return signature.sign();
    }

    /**
     * 鏍￠�?
     *
     * @param data
     *            寰呮牎楠屾暟鎹�
     * @param publicKey
     *            鍏�?
     * @param sign
     *            鏁板瓧绛惧悕
     *
     * @return boolean 鏍￠獙鎴愬姛杩斿洖true 澶辫触杩斿洖false
     * @throws Exception
     *
     */
    public static boolean verify(byte[] data, byte[] publicKey, byte[] sign)
            throws Exception {

        // 杞崲鍏挜鏉愭�?
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);

        // 瀹炰緥鍖栧瘑閽ュ伐鍘�?
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

        // 鐢熸垚鍏挜
        PublicKey pubKey = keyFactory.generatePublic(keySpec);

        // 瀹炰緥鍖朣ignature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);

        // 鍒濆鍖朣ignature
        signature.initVerify(pubKey);

        // 鏇存�?
        signature.update(data);

        // 楠岃�?
        return signature.verify(sign);
    }

    /**
     * 鍔犲�?<br>
     * 鐢ㄥ叕閽ュ姞瀵�
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] encrytByPublicKey(byte[] data, String key)
        throws Exception {
        //瀵瑰叕閽ヨВ�?��
        byte[] keyBytes = decryptBASE64(key);

        //鍙栧緱鍏挜
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);

        //瀵规暟鎹姞瀵�
        Cipher cipher = Cipher.getInstance(MODE);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.length;  
        ByteArrayOutputStream out = new ByteArrayOutputStream();  
        int offSet = 0;  
        byte[] cache;  
        int i = 0;  
        // 对数据分段加�?  
        while (inputLen - offSet > 0) {  
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {  
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);  
            } else {  
                cache = cipher.doFinal(data, offSet, inputLen - offSet);  
            }  
            out.write(cache, 0, cache.length);  
            i++;  
            offSet = i * MAX_ENCRYPT_BLOCK;  
        }  
        byte[] encryptedData = out.toByteArray();  
        out.close();  
        return encryptedData;  
    }

    /**
     * 瑙ｅ�?<br>
     * 鐢ㄥ叕閽ヨВ�?��
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {
        //瀵瑰瘑閽ヨВ�?��
        byte[] keyBytes = decryptBASE64(key);

        //鍙栧緱鍏挜
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);

        //瀵规暟鎹В�?��
        Cipher cipher = Cipher.getInstance(MODE);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);

        return cipher.doFinal(data);
    }

    /**
     * 鍔犲�?<br>
     * 鐢ㄧ閽ュ姞瀵�
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
        //瀵瑰瘑閽ヨВ�?��
        byte[] keyBytes = decryptBASE64(key);

        //鍙栧緱绉�?�?
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

        //瀵规暟鎹姞瀵�
        Cipher cipher = Cipher.getInstance(MODE);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 瑙ｅ�?<br>
     * 鐢ㄧ閽ヨВ�?��
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
        //瀵瑰瘑閽ヨВ�?��
        byte[] keyBytes = decryptBASE64(key);

        //鍙栧緱绉�?�?
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

        //瀵规暟鎹В�?��
        Cipher cipher = Cipher.getInstance(MODE);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        int inputLen = data.length;

        int offset = 0;

        byte[] cache;

        int i = 0;

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(data, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            Log.d(TAG, "parse i " + i);
            offset = i * MAX_DECRYPT_BLOCK;
        }

        byte[] deDataBytes = out.toByteArray();

        out.close();

        return deDataBytes;
    }

    /**
     * 鍙栧緱绉�?�?
     *
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap)
            throws Exception {

        Key key = (Key) keyMap.get(PRIVATE_KEY);

        return encryptBASE64(key.getEncoded());
    }

    /**
     * 浠庢枃浠朵腑鍔犺浇绉�?�?
     * @param keyFileName 绉侀挜鏂囦欢鍚�
     * @return String
     * @throws Exception
     */
    public static String loadKeyF(String keyFileName) throws Exception {

        File file = new File(keyFileName);
        if (!file.exists()) {
            Log.v("xxx","no file");
            return null;
        }

        BufferedReader br = new BufferedReader(new FileReader(keyFileName));

        try {

            String readLine = null;
            StringBuilder sb = new StringBuilder();
            while ((readLine = br.readLine()) != null) {
                if (readLine.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(readLine);
                    sb.append('\r');
//                    Log.d(TAG, sb.toString());
                }
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            br.close();
        }
    }



    /**
     * 鍙栧緱鍏挜
     *
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap)
            throws Exception {

        Key key = (Key) keyMap.get(PUBLIC_KEY);

        return encryptBASE64(key.getEncoded());
    }
}